package com.bluebench.bigul.service.resolver;

import java.io.IOException;
import java.io.StringReader;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import com.bluebench.bigul.domain.Element;
import com.bluebench.bigul.domain.Element.Attribute;
import com.bluebench.bigul.domain.Element.Parameter;
import com.bluebench.bigul.domain.Invocation;
import com.bluebench.bigul.domain.Tag;
import com.bluebench.bigul.exception.GraphicInitializationException;
import com.bluebench.bigul.service.handler.GenericHandler;
import com.bluebench.bigul.utility.FileToStringConversion;
import com.bluebench.bigul.utility.annotation.AnnotationUtil;
import com.bluebench.bigul.utility.annotation.Assign;

public abstract class DefaultExpressionResolver implements Resolver{
	
	public static final int ELEMENT_RESOLVER=0;
	public static final int ATTRIBUTE_RESOLVER=1;
	public static final int PARAMETER_RESOLVER=2;
	public static final int PRIMITIVE_RESOLVER=3;
	public static final int SUBSTITUTION_RESOLVER=4;
	public static final int GOBAL_RESOLVER=5;
	public static final int DEFAULT_RESOLVER=6;
	public static final int COLLECTION_RESOLVER=7;
	
	protected static boolean IS_INITIALIZED=false;
	
	protected static final String defaultXml="D:\\default.xml";
	private static final GenericHandler handler;
	
	protected Tag tag;
	
	static
	{
		handler = new GenericHandler();		
	}	
	
		
	
	
	@Override
	public void resolve() throws GraphicInitializationException{
	
		if(getDefaultElements().isEmpty()){
			try {				
				build(FileToStringConversion.convertToString(defaultXml));
				Map<String,Object> defaultElements = getDefaultElements();
				Set<String> defaultkeys = defaultElements.keySet();
				
				for(String k : defaultkeys){
					ElementResolver eResolver = new ElementResolver();
					eResolver.set((Element)defaultElements.get(k));
					eResolver.resolve();
				}				
				
				for(String k : defaultkeys){
					PrimitiveExpressionResolver primResolver = new PrimitiveExpressionResolver();
					primResolver.set((Element)defaultElements.get(k));
					primResolver.resolve();
				}
				
				/**
				 * TODO : This piece of code has no effect. it does not replace the invoked element back to the defaultElementsMap 
				 */
				for(String k : defaultkeys){
					
					Object o = defaultElements.get(k);
					Element e = (Element)o;
					Invocation i = e.getInvocation();
					if(i!=null){
						i.setField(e.getField());
						Object defElement = i.invoke();
						o = defElement;
					}			
					
				}				
				
			} catch (IOException e) {
				System.out.println(e.getMessage());
			} catch (SecurityException e) {				
				e.printStackTrace();
			} catch (IllegalArgumentException e) {				
				e.printStackTrace();
			} catch (ClassNotFoundException e) {				
				e.printStackTrace();
			} catch (NoSuchMethodException e) {				
				e.printStackTrace();
			} catch (IllegalAccessException e) {				
				e.printStackTrace();
			} catch (InvocationTargetException e) {				
				e.printStackTrace();
			} catch (InstantiationException e) {				
				e.printStackTrace();
			} catch (NoSuchFieldException e) {				
				e.printStackTrace();
			}
		}		
		
	}
	
	
	
		
	
	/*
	 * TODO : This is no the best place details of Default Element Resolution. Rather this code must be encapsulated in the 
	 * class DefaultElementResolver and the DefaultElementResolver may be used here. Although it would be better to use 
	 * an Orchestration service that would ensure a proper order of execution of resolvers. We also know that after 
	 * execution of each resolver we have to execute the PrimitiveExpressionResolver. The Orchestration Service can 
	 * ensure using a try-final{} block that the PrimitiveExpressionResolver is always executed.  
	 */
	/*protected void initialize() throws NoSuchFieldException{
		
	}*/
		
	@Override
	public void set(Tag t) {
		switch(t.getElementType()){
		case ELEMENT:
			tag = (Element)t;
			break;
		case ATTRIBUTE:
			tag = (Element.Attribute)t;
			break;
		case PARAMETER:
			tag = (Element.Parameter)t;
			break;
		default:	
		}
	}
	
	
	@Override
	public Resolver build(String templateXmlStr) {
			
		SAXParserFactory factory = SAXParserFactory.newInstance();
	    SAXParser parser;	    
	    
	    try {
	    	parser = factory.newSAXParser();	    	
		    parser.parse(new InputSource(new StringReader(templateXmlStr)),handler);
		    tag = handler.getRoot();
		    		    
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		}		
		
	    return this;
	}
	
	@Override
	public void resolveExpression(String expression) {	
		
		Element resolvedElement=null;
		
		/*
		 * TODO : Currently we are resolving primitive expressions separately using PrimitiveExpressionResolver. 
		 * We pass the root element to primitive expression resolver and it resolves the entire hierarchy. 
		 * Instead of this approach we can include another else-if condition here that resolves the 
		 * expression starting with _ (underscore) and use the resolvePrimitive() method of the PrimitiveExpressionResolver.
		 * This way we will resolve primitive at the time of resolving elements  
		 */
		if(expression.startsWith("#global")){
			resolvedElement = getGlobalElements().get(expression.replace("#global.", ""));	
		}else if(expression.startsWith("#default")){
			resolvedElement = (Element)getDefaultElements().get(expression.replace("#default.", ""));			
		}else{
			resolvedElement = getElementsMap().get(expression.replace("#", ""));
		}		
		
		if(resolvedElement!=null){			
						
			List<Map<String,Object>> fieldproperties = AnnotationUtil.scanField(tag.getClass(),Assign.class);
			
			for(Map<String,Object> fieldPropertyMap : fieldproperties){
				
				String value = fieldPropertyMap.get(AnnotationUtil.VALUE).toString();
								
				switch(tag.getElementType()){
					
					case ELEMENT:
						
						if(value.equals("element")){
							Element e = (Element)tag;
							if((!resolvedElement.getElements().isEmpty())){
								e.setElements(resolvedElement.getElements());
							}
							e.setType(resolvedElement.getType());
							e.setInvocation(resolvedElement.getInvocation());
							
						}else if(value.equals("attribute") && (!resolvedElement.getAttributes().isEmpty())){
							Element e = (Element)tag;
							e.addAttributes(resolvedElement.getAttributes());
							e.setType(resolvedElement.getType());
							e.setInvocation(resolvedElement.getInvocation());
							
						}
						break;
					
					case ATTRIBUTE:
					
						if(value.equals("parameter")){
							Attribute a = ((Attribute)tag);
							a.addParameter(resolvedElement);
							
						}
						break;
					
					case PARAMETER:
					
						if(value.equals("element")){
							((Parameter)tag).setValue(resolvedElement);
							//Possible issue : The resolved element is of type list, whereas the tag is of type listitem. This may cause problem during processing. 
							tag.setType(resolvedElement.getType());
							
						}
						break;
				
					}				
				
				
			}
			
			
		}
		
	}

	
	@Override
	public boolean validate(String expression) {
		return handler.isKey(expression);
	}
	
	public Map<String,Element> getElementsMap(){
		return handler.getElementsMap();
	}
	
	public static Map<String,Element> getGlobalElements(){
		return handler.getGlobalElementsMap();
	}
	
	public Map<String,Object> getDefaultElements(){
		return handler.getDefaultElementsMap();
	}
	
	public Element getRoot(){
		return handler.getRoot();
	}
	
	
	public static Resolver getInstance(int type) throws GraphicInitializationException{
		switch(type){
			case DefaultExpressionResolver.ELEMENT_RESOLVER:
				return new ElementResolver();			
			case DefaultExpressionResolver.ATTRIBUTE_RESOLVER:
				return new AttributeResolver();
			case DefaultExpressionResolver.PARAMETER_RESOLVER:
				return new ParameterResolver();
			case DefaultExpressionResolver.PRIMITIVE_RESOLVER:
				return new PrimitiveExpressionResolver();
			case DefaultExpressionResolver.SUBSTITUTION_RESOLVER:
				return new SubstitutionExpressionResolver();
			case DefaultExpressionResolver.GOBAL_RESOLVER:
				return new GlobalElementResolver();
			case DefaultExpressionResolver.COLLECTION_RESOLVER:
				return new CollectionResolver();
			default:
				return null;
		}
	}

	
	
	
}
